<?php
/**
 * InventoryRepo.php
 *
 * @copyright   2023 thingsworld.com.cn - All Rights Reserved
 * @link        http://thingsworld.com.cn/
 * @author      stiffer.chen <chenl@thingsworld.com.cn>
 * @created     2023-05-16 15:57:26
 * @modified    2023-05-16 15:57:26
 *
 */


/**
 * 调拨
 * 撤销: 原仓库管理员
 * 拒绝: 目标仓库管理员
 * 办理: 目标仓库管理员
 */

namespace App\Repositories;

use App\Models\DictModel;
use App\Models\Inventory;
use App\Models\InventoryAllot;
use App\Models\InventoryAuditHistory;
use App\Models\InventoryHistory;
use App\Models\PartCategory;
use App\Models\UserInventory;
use App\Models\UserModel;
use App\Models\Warehouse;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use WebSocket\Exception;

class InventoryRepo
{
    const TYPE = '备件库';

    const ACTION_STORAGE = '入库';
    const ACTION_OUTSIDE = '出库';
    const ACTION_ALLOT = '调拨';

    const STATUS_PENDING_CODE = 'pending';
    const STATUS_PENDING_TEXT = '待办理';

    const STATUS_PROCESSING_CODE = 'processing';
    const STATUS_PROCESSING_TEXT = '办理中';

    const STATUS_PROCESSED_CODE = 'processed';
    const STATUS_PROCESSED_TEXT = '已办理';

    const STATUS_REJECT_CODE = 'reject';
    const STATUS_REJECT_TEXT = '已拒绝';

    const STATUS_CANCEL_CODE = 'cancel';
    const STATUS_CANCEL_TEXT = '已撤销';

    const FILTER_TYPE_WEEK = 'week';
    const FILTER_TYPE_MONTH = 'month';
    const FILTER_TYPE_DATE_SELECT = 'date_select';

    const FILTER_TYPES = [
        self::FILTER_TYPE_WEEK,
        self::FILTER_TYPE_MONTH,
        self::FILTER_TYPE_DATE_SELECT
    ];

    const STATUS = [
        ['code' => self::STATUS_PENDING_CODE, 'text' => self::STATUS_PENDING_TEXT],
        ['code' => self::STATUS_PROCESSING_CODE, 'text' => self::STATUS_PROCESSING_TEXT],
        ['code' => self::STATUS_PROCESSED_CODE, 'text' => self::STATUS_PROCESSED_TEXT],
        ['code' => self::STATUS_REJECT_CODE, 'text' => self::STATUS_REJECT_TEXT],
        ['code' => self::STATUS_CANCEL_CODE, 'text' => self::STATUS_CANCEL_TEXT],
    ];

    const TYPES_ALLOT_CODE = 'allot';
    const TYPES_ALLOT_TEXT = '调拨';

    const TYPES_ALLOCATION_CODE = 'allocation';
    const TYPES_ALLOCATION_TEXT = '分配';

    const TYPES_APPLY_CODE = 'apply';
    const TYPES_APPLY_TEXT = '申领';

    const TYPES_BACK_CODE = 'back';
    const TYPES_BACK_TEXT = '退回';

    const TYPES = [
        ['code' => self::TYPES_ALLOT_CODE, 'text' => self::TYPES_ALLOT_TEXT],
        ['code' => self::TYPES_ALLOCATION_CODE, 'text' => self::TYPES_ALLOCATION_TEXT],
        ['code' => self::TYPES_APPLY_CODE, 'text' => self::TYPES_APPLY_TEXT],
        ['code' => self::TYPES_BACK_CODE, 'text' => self::TYPES_BACK_TEXT],
    ];

    const ABOUT_ME_AUDIT_CODE = 'audit';
    const ABOUT_ME_CREATE_CODE = 'create';

    const ABOUTS = [
        self::ABOUT_ME_AUDIT_CODE,
        self::ABOUT_ME_CREATE_CODE
    ];

    public static function params()
    {
        $storageTypeCode = 'inventory-st-type';
        $outTypeCode = 'inventory-out-type';
        $code = [$storageTypeCode, $outTypeCode];

        $builder = DictModel::query()
            ->from('dict as d')
            ->join('dict_data as dd', 'dd.dict_id', '=', 'd.id')
            ->select(['dd.name', 'd.code'])
            ->whereIn('d.code', $code);
        $params = $builder->get();

        $storageType = [];
        $outType = [];

        foreach ($params as $param) {
            $code = $param->code;
            if ($code == $storageTypeCode) {
                $storageType[] = $param->name;
            }
            if ($code == $outTypeCode) {
                $outType[] = $param->name;
            }
        }

        return [
            'storage_types' => $storageType,
            'out_types' => $outType
        ];
    }

    public static function inventoryAuditStatus()
    {
        return [
            ['code' => self::STATUS_PENDING_CODE, 'title' => self::STATUS_PENDING_TEXT],
            ['code' => self::STATUS_PROCESSING_CODE, 'title' => self::STATUS_PROCESSING_TEXT],
            ['code' => self::STATUS_PROCESSED_CODE, 'title' => self::STATUS_PROCESSED_TEXT],
            ['code' => self::STATUS_REJECT_CODE, 'title' => self::STATUS_REJECT_TEXT],
            ['code' => self::STATUS_CANCEL_CODE, 'title' => self::STATUS_CANCEL_TEXT]
        ];
    }

    public static function userInventoryBack($id)
    {
        $userInventory = UserInventory::find($id);
        if (empty($userInventory) || $userInventory->user_id != registry('user_id')) {
            throw new \Exception("不存在这样的个人备件库");
        }

        $targetWarehouseId = intval(request('target_warehouse_id', ''));
        $quantity = intval(request('quantity', ''));
        $description = trim(request('description', ''));

        if (!$targetWarehouseId) {
            throw new \Exception('请选择要退回的仓库');
        }
        $warehouse = Warehouse::find($targetWarehouseId);
        if (empty($warehouse)) {
            throw new \Exception('目标仓库不存在');
        }
        if ($quantity <= 0) {
            throw new \Exception('退回数量错误');
        }
        $activeQuantity = $userInventory->inventory - $userInventory->disabled_inventory;
        if ($quantity > $activeQuantity) {
            throw new \Exception('退回数量错误');
        }

        $cid = $userInventory->c_id;

        $inventory = Inventory::byCid($cid)->byWarehouseId($targetWarehouseId)->first();
        if (empty($inventory)) {
            $inventory = new Inventory();
        }

        $partCategory = PartCategory::find($cid);

        $inventoryData = [
            'warehouse_id' => $targetWarehouseId,
            'storage_type' => $inventory->storage_type ?? self::TYPES_BACK_TEXT,
            'c_id' => $cid,
            'c_number' => $inventory->c_number ?? $partCategory->c_number ?? '',
            'type' => $inventory->type ?? $userInventory->type ?? '',
            'model' => $inventory->model ?? $userInventory->model ?? '',
            'uom' => $inventory->uom ?? $userInventory->uom ?? '',
            'sale_price' => $inventory->sale_price ?? $userInventory->sale_price ?? '',
            'out_price' => $inventory->out_price ?? $userInventory->out_price ?? '',
            'description' => $inventory->description ?? $description,
            'inventory' => ($inventory->inventory ?? 0) + $quantity,
            'create_user_id' => $inventory->create_user_id ?? registry('user_id')
        ];

        $inventory->fill($inventoryData);
        $inventory->saveOrFail();

        $history = new InventoryHistory();
        $historyData = [
            'target_warehouse_id' => $targetWarehouseId,
            'target_warehouse_type' => '备件库',
            'count' => $quantity,
            'action' => self::TYPES_BACK_TEXT,
            'description' => $description,
            'create_user_id' => registry('user_id')
        ];
        $history->fill($historyData);
        $history->saveOrFail();

        $userInventory->disabled_inventory += $quantity;
        $userInventory->saveOrFail();
    }

    public static function getUserList()
    {
        $perPage = (int)request('limit');
        if ($perPage < 1) {
            $perPage = 10;
        }

        $keyword = trim(request('keyword', ''));
        $warehouseId = intval(request('warehouse_id', ''));

        $userId = intval(request('user_id', 0));
        $userWarehouseIds = Warehouse\User::query()->where('user_id', $userId)->pluck('warehouse_id')->toArray();
        $warehouseIds = [];
        if ($userWarehouseIds) {
            $warehouseIds = $userWarehouseIds;
        }
        if ($warehouseId) {
            $warehouseIds[] = $warehouseId;
        }
        $warehouseIds = array_unique($warehouseIds);

        $builder = UserInventory::query();
        // 没有传仓库管理员ID时，查询自己的
        if (!$userWarehouseIds) {
            $builder->where('user_id', registry('user_id'));
        }
        if ($keyword) {
            $builder->where(function ($query) use ($keyword) {
                $query->where('c_number', 'like', "%{$keyword}%")
                    ->orWhere('type', 'like', "%{$keyword}%")
                    ->orWhere('model', 'like', "%{$keyword}%");
            });
        }
        if ($warehouseIds) {
            $builder->whereIn('warehouse_id', $warehouseIds);
        }
        $results = $builder->paginate($perPage);
        $results->transform(function ($item) {
            return self::getUserInventoryDetail($item);
        });
        return $results;
    }

    public static function getUserInventoryDetail($inventory)
    {
        return [
            'id' => $inventory->id,
            'type' => $inventory->type,
            'number' => $inventory->c_number,
            'name' => $inventory->c_name,
            'part_category_id' => $inventory->c_id,
            'part_category_name' => $inventory->c_name,
            'part_category_model' => $inventory->model,
            'part_category_uom' => $inventory->uom,
            'quantity' => $inventory->inventory,
            'disabled_inventory' => $inventory->disabled_inventory
        ];
    }

    public static function getList()
    {
        $perPage = (int)request('limit');
        if ($perPage < 1) {
            $perPage = 10;
        }
        $type = trim(request('type', ''));
        $keyword = trim(request('keyword', ''));
        $storageType = trim(request('storage_type', ''));
        $warehouseId = intval(request('warehouse_id', 0));
        $inventoryOrder = strtolower(trim(request('inventory_order', '')));
        $builder = Inventory::query();
        $builder->with(['part_category', 'warehouse', 'user']);
        if ($keyword) {
            $builder->where(function ($query) use ($keyword) {
                $query->where('c_number', 'like', "%{$keyword}%")
                    ->orWhere('model', 'like', "%{$keyword}%")
                    ->orWhere('description', 'like', "%{$keyword}%");
            });
        }
        if ($type) {
            $builder->where('type', $type);
        }
        if ($inventoryOrder == 'asc') {
            $builder->orderBy('inventory');
        } elseif ($inventoryOrder == 'desc') {
            $builder->orderByDesc('inventory');
        } elseif ($inventoryOrder == 'zero') {
            $builder->where('inventory', '=', 0);
        } elseif ($inventoryOrder == 'nozero') {
            $builder->where('inventory', '>', 0);
        }

        if ($storageType) {
            $builder->where('storage_type', $storageType);
        }
        if ($warehouseId) {
            $builder->where('warehouse_id', $warehouseId);
        }

        $sort = strtolower(trim(request('sort', 'inventory.update_time')));
        $order = strtolower(trim(request('order', 'desc')));
        if (!in_array($order, ['asc', 'desc'])) {
            $order = 'desc';
        }

        $builder->orderBy($sort, $order);

        $results = $builder->paginate($perPage);
        $results->transform(function ($item) {
            return self::getDetail($item);
        });

        return $results;
    }

    private static function getDetail($inventory)
    {
        $warehouse = $inventory->warehouse;
        $partCategory = $inventory->part_category;
        $user = $inventory->user;
        // is_manager
        $warehouseId = $warehouse->id;
        return [
            'id' => $inventory->id,
            'c_id' => $inventory->c_id,
            'storage_type' => $inventory->storage_type,
            'c_number' => $inventory->c_number,
            'c_name' => $partCategory->name ?? '',
            'c_status' => $partCategory->status ?? 0,
            'c_deleted' => empty($partCategory) ? 1 : 0,
            'model' => $inventory->model,
            'uom' => $inventory->uom,
            'warehouse_id' => $warehouseId,
            'warehouse_name' => $warehouse->name,
            'inventory' => $inventory->inventory,
            'safe_inventory' => $inventory->safe_inventory,
            'sale_price' => $inventory->sale_price,
            'out_price' => $inventory->out_price,
            'description' => $inventory->description,
            'create_user_id' => $inventory->create_user_id,
            'create_user_name' => $user->realname,
            'is_manager' => self::checkSelfWarehouse($inventory->id),
            'type_name' => $inventory->type,
            'status' => 1
        ];
    }

    public static function allocation()
    {
        $data = self::requestAllocationData();

        $allotData = [
            'number' => $data['number'],
            'source_warehouse_id' => $data['warehouse_id'],
            'target_user_id' => $data['user_id'],
            'create_user_id' => registry('user_id'),
            'status' => self::STATUS_PENDING_CODE,
            'type' => self::TYPES_ALLOCATION_CODE,
            'description' => $data['description']
        ];

        $items = $data['items'];
        $allotItems = [];

        foreach ($items as $item) {
            $allotItems[] = new InventoryAllot\Item([
                'inventory_id' => $item['id'],
                'quantity' => $item['quantity'],
                'action_quantity' => 0
            ]);

            // 库存减去数量
            $inventory = Inventory::find($item['id']);
            if (!empty($inventory)) {
                $inventory->inventory = $inventory->inventory - $item['quantity'];
                $inventory->saveorFail();
            }
        }

        $allot = new InventoryAllot();
        $allot->fill($allotData);
        $allot->saveOrFail();

        $allot->items()->delete();
        $allot->items()->saveMany($allotItems);

        self::pushNotifyMessage($allot->id);
        self::addAuditHistory($allot->id);
    }

    // 申领
    public static function apply()
    {
        $data = self::requestApplyData();
        $allot = new InventoryAllot();
        $allotData = [
            'number' => $data['number'],
            'source_warehouse_id' => $data['source_warehouse_id'],
            'target_user_id' => $data['target_user_id'],
            'create_user_id' => registry('user_id'),
            'description' => $data['description'],
            'type' => self::TYPES_APPLY_CODE,
            'status' => self::STATUS_PENDING_CODE
        ];
        $items = [];
        foreach ($data['items'] as $item) {
            $inventoryId = $item['id'];
            $quantity = $item['quantity'];
            $items[] = new InventoryAllot\Item([
                'inventory_id' => $inventoryId,
                'quantity' => $quantity,
                'action_quantity' => 0
            ]);

            $inventory = Inventory::find($inventoryId);
            if (!empty($inventory)) {
                $inventory->inventory = $inventory->inventory - $quantity;
                $inventory->saveOrFail();
            }
        }

        $allot->fill($allotData);
        $allot->saveOrFail();

        $allot->items()->delete();
        $allot->items()->saveMany($items);

        self::pushNotifyMessage($allot->id);
        self::addAuditHistory($allot->id);
    }

    // 调拨数据提交
    public static function allot()
    {
        $data = self::requestAllotData();
        $allot = new InventoryAllot();
        $allotData = [
            'number' => $data['number'],
            'source_warehouse_id' => $data['source_warehouse_id'],
            'target_warehouse_id' => $data['target_warehouse_id'],
            'create_user_id' => registry('user_id'),
            'description' => $data['description'],
            'type' => self::TYPES_ALLOT_CODE,
            'status' => self::STATUS_PENDING_CODE
        ];
        $items = [];
        foreach ($data['items'] as $item) {
            $items[] = new InventoryAllot\Item([
                'inventory_id' => $item['id'],
                'quantity' => $item['quantity'],
                'action_quantity' => 0
            ]);

            // 库存减去数量
            $inventory = Inventory::find($item['id']);
            if (!empty($inventory)) {
                $inventory->inventory = $inventory->inventory - $item['quantity'];
                $inventory->saveorFail();
            }
        }

        $allot->fill($allotData);
        $allot->saveOrFail();

        $allot->items()->delete();
        $allot->items()->saveMany($items);

        self::pushNotifyMessage($allot->id);
        self::addAuditHistory($allot->id);
    }

    public static function auditList()
    {
        $perPage = (int)request('limit');
        if ($perPage < 1) {
            $perPage = 10;
        }

        $keyword = trim(request('keyword', ''));
        $warehouseId = intval(request('warehouse_id', ''));
        $type = trim(request('type', ''));
        $status = trim(request('status', ''));
        $startDate = trim(request('start_date', ''));
        $endDate = trim(request('end_date', ''));
        $aboutMe = trim(request('about_me', ''));

        $filterType = strtolower(request('filter_type') ?? '');
        if (!in_array($filterType, self::FILTER_TYPES)) {
            $filterType = self::FILTER_TYPE_DATE_SELECT;
        }

        if ($filterType == self::FILTER_TYPE_WEEK) {
            $startDate = Carbon::now()->startOfWeek()->toDateString();
            $endDate = Carbon::now()->endOfWeek()->toDateString();
        } elseif ($filterType == self::FILTER_TYPE_MONTH) {
            $startDate = Carbon::now()->startOfMonth()->toDateString();
            $endDate = Carbon::now()->endOfMonth()->toDateString();
        }

        $builder = InventoryAllot::query();
        $builder->orderByDesc('create_time');
        $builder->with(['items', 'source_warehouse', 'target_warehouse', 'user']);
        if ($keyword) {
            $builder->where('number', 'like', "%{$keyword}%");
        }
        if ($warehouseId) {
            $builder->where(function ($query) use ($warehouseId) {
                $query->where('source_warehouse_id', $warehouseId)
                    ->orWhere('target_warehouse_id', $warehouseId);
            });
        }
        if ($type) {
            $builder->where('type', $type);
        }
        if ($status) {
            $builder->where('status', $status);
        }
        try {
            if ($startDate) {
                $startDate = Carbon::parse($startDate)->toDateString() . ' 00:00:00';
            }
        } catch (\Exception $e) {
            $startDate = '';
        }
        try {
            if ($endDate) {
                $endDate = Carbon::parse($endDate)->toDateString() . ' 23:59:59';
            }
        } catch (\Exception $e) {
            $endDate = '';
        }
        if ($startDate) {
            $startDate = Carbon::parse($startDate)->timestamp;
            $builder->where('create_time', '>=', $startDate);
        }
        if ($endDate) {
            $endDate = Carbon::parse($endDate)->timestamp;
            $builder->where('create_time', '<=', $endDate);
        }
        $currentUserId = registry('user_id');
        if ($aboutMe == self::ABOUT_ME_CREATE_CODE) {
            $builder->where('create_user_id', $currentUserId);
        }
        if ($aboutMe == self::ABOUT_ME_AUDIT_CODE) {
            $builder->where('audit_user_id', $currentUserId);
        }
        $results = $builder->paginate($perPage);
        $results->transform(function ($item) {
            return self::getAllotDetail($item);
        });
        return $results;
    }

    /**
     *
     * 拒绝: 已办理、已拒绝的不能拒绝
     * 退回数量 = 申请数量 - 已办理数量
     * @param $id
     * @throws \Exception
     */
    public static function auditReject($id)
    {
        $inventoryAllot = InventoryAllot::find($id);

        $description = trim(request('description', ''));

        if (empty($inventoryAllot)) {
            throw new \Exception("审核数据不存在: {$id}");
        }

        if (!self::showAllotRefuse($inventoryAllot)) {
            throw new \Exception("当前审核单不允许拒绝");
        }

        $type = $inventoryAllot->type;

        if ($type == self::TYPES_ALLOT_CODE) {
            self::rejectAllot($id);
        } elseif ($type == self::TYPES_ALLOCATION_CODE) {
            self::rejectAllocation($id);
        } elseif ($type == self::TYPES_APPLY_CODE) {
            self::rejectApply($id);
        }

        $inventoryAllot->status = self::STATUS_REJECT_CODE;
        $inventoryAllot->description = $description;
        $inventoryAllot->audit_user_id = registry('user_id');
        $inventoryAllot->saveOrFail();
        self::pushNotifyMessage($inventoryAllot->id);
        self::addAuditHistory($inventoryAllot->id);
    }

    private static function rejectApply($id)
    {
        $inventoryAllot = InventoryAllot::find($id);
        self::rejectRecoverQuantity($inventoryAllot);
    }

    /**
     * 分配拒绝: 超级管理员或目标用户
     *
     * @param $id
     */
    private static function rejectAllocation($id)
    {
        $inventoryAllot = InventoryAllot::find($id);
        self::rejectRecoverQuantity($inventoryAllot);
    }

    // 调拨拒绝
    private static function rejectAllot($id)
    {
        $inventoryAllot = InventoryAllot::find($id);
        self::rejectRecoverQuantity($inventoryAllot);
    }

    // 拒绝后，恢复库存
    private static function rejectRecoverQuantity($inventoryAllot)
    {
        $items = $inventoryAllot->items;
        $backItems = [];
        foreach ($items as $item) {
            $itemActionQuantity = $item->action_quantity;
            $itemQuantity = $item->quantity;
            if ($itemActionQuantity >= $itemQuantity) {
                continue;
            }
            $backItems[] = [
                'inventory_id' => $item->inventory_id,
                'quantity' => $itemQuantity - $itemActionQuantity
            ];
        }
        if ($backItems) {
            foreach ($backItems as $backItem) {
                $inventoryId = $backItem['inventory_id'];
                $quantity = $backItem['quantity'];

                $itemInventory = Inventory::find($inventoryId);
                $itemInventory->inventory = $itemInventory->inventory + $quantity;
                $itemInventory->saveOrFail();
            }
        }
    }

    // 出入库撤销
    public static function auditCancel($id)
    {
        $inventoryAllot = InventoryAllot::find($id);
        if (empty($inventoryAllot)) {
            throw new \Exception("审核数据不存在: {$id}");
        }
        if (!self::showAllotCancel($inventoryAllot)) {
            throw new \Exception('当前审核单不允许撤销');
        }
        $type = $inventoryAllot->type;
        if ($type == self::TYPES_ALLOT_CODE) {
            self::auditAllotCancel($inventoryAllot);
        } elseif ($type == self::TYPES_ALLOCATION_CODE) {
            self::auditAllocationCancel($inventoryAllot);
        } elseif ($type == self::TYPES_APPLY_CODE) {
            self::auditApplyCancel($inventoryAllot);
        }
    }

    // 申领撤销
    private static function auditApplyCancel($allot)
    {
        $targetUserId = $allot->target_user_id;
        $currentUserId = registry('user_id');
        if ($currentUserId != 1 && $currentUserId != $targetUserId) {
            throw new \Exception('仅申领人可以撤销');
        }
        self::auditAllotCancel($allot);
    }

    // 分配撤销
    private static function auditAllocationCancel($allot)
    {
        self::auditAllotCancel($allot);
    }

    /**
     * 调拨撤销
     * 仅原仓库管理员 + 超级管理员可以操作
     * 原仓库库存恢复
     * 审核单状态变更为: 已撤销
     * @param $allot
     */
    private static function auditAllotCancel($allot)
    {
        $items = $allot->items;
        $cancelItems = [];
        foreach ($items as $item) {
            $cancelItems[] = [
                'inventory_id' => $item->inventory_id,
                'quantity' => $item->quantity - $item->action_quantity
            ];
        };
        foreach ($cancelItems as $cancelItem) {
            $inventoryId = $cancelItem['inventory_id'];
            $quantity = $cancelItem['quantity'];

            $itemInventory = Inventory::find($inventoryId);
            $itemInventory->inventory = $itemInventory->inventory + $quantity;
            $itemInventory->saveOrFail();
        }

        $description = trim(request('description', ''));
        $allot->status = self::STATUS_CANCEL_CODE;
        $allot->description = $description;
        $allot->saveOrFail();

        $allotId = $allot->id;
        self::pushNotifyMessage($allotId);
        self::addAuditHistory($allotId);
    }

    private static function auditBack($status, $id)
    {
        $allot = InventoryAllot::find($id);
        if (empty($allot)) {
            throw new \Exception('审核数据不存在');
        }
        $items = $allot->items;

        $backItems = [];
        foreach ($items as $item) {
            $backItems[] = [
                'inventory_id' => $item->inventory_id,
                'quantity' => $item->quantity - $item->action_quantity
            ];
        }

        foreach ($backItems as $item) {
            $inventoryId = $item['inventory_id'];
            $quantity = $item['quantity'];

            $inventory = Inventory::find($inventoryId);
            if (empty($inventory) || $quantity <= 0) {
                continue;
            }
            $inventory->inventory = $inventory->inventory + $quantity;
            $inventory->saveOrFail();
        }
        $allot = InventoryAllot::find($id);
        $allot->status = $status;
        $allot->description = request('description', '');
        $allot->audit_user_id = registry('user_id');
        $allot->saveOrFail();
    }

    /**
     * 出入库办理
     * 不能操作的状态: 已撤销、已拒绝、已办理
     * @param $id
     * @throws \Exception
     */
    public static function auditAccept($id)
    {
        $allot = InventoryAllot::find($id);
        if (empty($allot)) {
            throw new \Exception("提交数据不存在(ID: {$id})");
        }
        $type = $allot->type;
        if ($type == self::TYPES_ALLOT_CODE) {
            self::auditAllotAccept($allot);
        } elseif ($type == self::TYPES_ALLOCATION_CODE) {
            self::auditAllocationAccept($allot);
        } elseif ($type == self::TYPES_APPLY_CODE) {
            self::auditAllocationAccept($allot);
        }
    }

    /**
     * 分配办理
     * @param $allot
     */
    private static function auditAllocationAccept($allot)
    {
        $targetUserId = $allot->target_user_id;
        $currentUserId = registry('user_id');
        $warehouseId = $allot->source_warehouse_id;

        $data = self::getAcceptData($allot);
        $description = $data['description'];
        $acceptItems = $data['items'];

        foreach ($acceptItems as $item) {
            $itemId = $item['id'];

            $itemInventoryId = $item['inventory_id'];
            $itemQuantity = $item['quantity'];

            $itemInventory = Inventory::find($itemInventoryId);
            if (empty($itemInventory)) {
                continue;
            }

            $itemCid = $itemInventory->c_id;

            $userInventory = UserInventory::byUserId($targetUserId)->byCid($itemCid)->byWarehouseId($warehouseId)->first();
            if (empty($userInventory)) {
                $userInventory = new UserInventory();
                $userInventoryData = [
                    'user_id' => $targetUserId,
                    'warehouse_id' => $warehouseId,
                    'c_id' => $itemCid,
                    'c_name' => $itemInventory->part_category->name ?? '',
                    'c_number' => $itemInventory->c_number,
                    'type' => $itemInventory->type,
                    'model' => $itemInventory->model,
                    'uom' => $itemInventory->uom,
                    'sale_price' => $itemInventory->sale_price,
                    'out_price' => $itemInventory->out_price,
                    'description' => $itemInventory->description,
                    'safe_inventory' => $itemInventory->safe_inventory,
                    'inventory' => $itemQuantity,
                    'disabled_inventory' => 0,
                    'create_user_id' => $currentUserId
                ];
                $userInventory->fill($userInventoryData);
                $userInventory->saveOrFail();
            } else {
                $userInventory->inventory = $userInventory->inventory + $itemQuantity;
                $userInventory->create_user_id = $currentUserId;
                $userInventory->saveOrFail();
            }

            // 添加办理数量
            $item = InventoryAllot\Item::find($itemId);
            if (empty($item)) {
                continue;
            }
            $item->action_quantity += $itemQuantity;
            $item->saveOrFail();
        }

        // 办理后的状态
        $status = self::getAcceptStatus($allot->id);

        $allot->status = $status;
        $allot->description = $description;
        $allot->audit_user_id = registry('user_id');
        $allot->saveOrFail();

        self::pushNotifyMessage($allot->id);
        self::addAuditHistory($allot->id);
    }

    /**
     * 调拨办理
     * 操作人: 目标仓库管理员
     * 状态: 已办理完成: 已办理, 未办理完成: 办理中
     * 目标仓库: 加库存
     * 审核明细: 加已办理数量, 更新状态
     * @param $allot
     */
    private static function auditAllotAccept($allot)
    {
        // 数据获取
        $data = self::getAcceptData($allot);
        $description = $data['description'];
        $acceptItems = $data['items'];

        $targetWarehouseId = $allot->target_warehouse_id;
        foreach ($acceptItems as $acceptItem) {
            // 明细ID，处理之后修改办理数量
            $itemId = $acceptItem['id'];
            // 办理数量
            $actionQuantity = $acceptItem['quantity'];
            $currentInventory = Inventory::find($acceptItem['inventory_id']); // 原仓库库存信息
            if (empty($currentInventory)) {
                continue;
            }
            // 判断数据是否存在: 目标仓库ID + 原仓库 c_id
            $targetInventory = Inventory::byWarehouseId($targetWarehouseId)->byCid($currentInventory->c_id)->first();
            if (empty($targetInventory)) {
                $targetInventory = new Inventory();
                $targetInventoryData = [
                    'warehouse_id' => $targetWarehouseId,
                    'storage_type' => $currentInventory->storage_type,
                    'c_id' => $currentInventory->c_id,
                    'c_number' => $currentInventory->c_number,
                    'type' => $currentInventory->type,
                    'model' => $currentInventory->model,
                    'uom' => $currentInventory->uom,
                    'sale_price' => $currentInventory->sale_price,
                    'out_price' => $currentInventory->out_price,
                    'description' => $currentInventory->description,
                    'inventory' => $actionQuantity,
                    'safe_inventory' => $currentInventory->safe_inventory,
                    'create_user_id' => registry('user_id')
                ];
                $targetInventory->fill($targetInventoryData);
                $targetInventory->saveOrFail();
            } else {
                $targetInventory->description = $description;
                $targetInventory->inventory = $targetInventory->inventory + $actionQuantity;
                $targetInventory->create_user_id = registry('user_id');
                $targetInventory->saveOrFail();
            }

            // 添加办理数量
            $item = InventoryAllot\Item::find($itemId);
            if (empty($item)) {
                continue;
            }
            $item->action_quantity += $actionQuantity;
            $item->saveOrFail();
        }

        // 办理后的状态
        $status = self::getAcceptStatus($allot->id);

        $allot->status = $status;
        $allot->description = $description;
        $allot->audit_user_id = registry('user_id');
        $allot->saveOrFail();

        self::pushNotifyMessage($allot->id);
        self::addAuditHistory($allot->id);
    }

    // 获取办理数据
    private static function getAcceptData($allot)
    {
        $data = request()->getContent();
        $requestArr = json_decode($data, true);

        $acceptItems = [];
        $description = $requestArr['description'] ?? '';

        $requestItems = $requestArr['items'] ?? [];
        if (empty($requestItems)) {
            throw new \Exception("办理数据为空， 请检查");
        }

        $allotItems = $allot->items->keyBy('id')->toArray();
        $allotItemIds = array_keys($allotItems);

        foreach ($requestItems as $requestItem) {
            $requestItemId = intval($requestItem['id'] ?? 0);
            $requestItemQuantity = intval($requestItem['quantity'] ?? 0);
            if (!$requestItemId || !$requestItemQuantity) {
                throw new \Exception("办理明细错误(ID: {$requestItemId}, 数量: {$requestItemQuantity})");
            }

            if (!in_array($requestItemId, $allotItemIds)) {
                throw new \Exception("提交数据非当前审核明细(ID: {$requestItemId})");
            }

            $currentItem = $allotItems[$requestItemId];
            $acceptQuantity = $currentItem['quantity'] - $currentItem['action_quantity'];
            if ($requestItemQuantity > $acceptQuantity) {
                throw new \Exception("提交数量错误(ID: {$requestItemId}, 办理数量: {$requestItemQuantity}, 可办理数量: {$acceptQuantity})");
            }
            $acceptItems[] = [
                'id' => $requestItemId,
                'inventory_id' => $currentItem['inventory_id'],
                'quantity' => $requestItemQuantity
            ];

        }

        return [
            'description' => $description,
            'items' => $acceptItems
        ];
    }

    // 获取办理后的状态
    private static function getAcceptStatus($id)
    {
        $statusAllot = InventoryAllot::find($id);
        $statusAllotItems = $statusAllot->items;
        $status = self::STATUS_PROCESSED_CODE;
        foreach ($statusAllotItems as $statusAllotItem) {
            if ($statusAllotItem->quantity > $statusAllotItem->action_quantity) {
                $status = self::STATUS_PROCESSING_CODE;
                break;
            }
        }
        return $status;
    }

    public static function auditInfo($id)
    {
        $allot = InventoryAllot::find($id);
        if (empty($allot)) {
            throw new \Exception("ID错误(#{$id})");
        }
        $allot->with(['items.inventory.part_category', 'source_warehouse.users', 'target_warehouse.users', 'user']);
        return self::getAllotDetail($allot);
    }

    public static function getAllotDetail($allot, $withItems = true)
    {
        $items = $allot->items;
        $quantity = $items->sum('quantity');
        $completeQuantity = $items->sum('action_quantity');

        $currentUserId = registry('user_id');
        $status = $allot->status;

        // 撤销按钮
        $showCancel = self::showAllotCancel($allot);
        // 拒绝按钮
        $showRefuse = self::showAllotRefuse($allot);
        // 审核按钮
        $showAudit = self::showAllotAudit($allot);

        $targetUserId = $allot->target_user_id;
        $targetUser = UserModel::find($targetUserId);
        $targetUserName = $targetUser->realname ?? '';

        $refuseReason = '';
        if ($allot->type == self::STATUS_REJECT_CODE) {
            $refuseReason = $allot->description;
        }

        $detail = [
            'id' => $allot->id,
            'number' => $allot->number,
            'apply_date' => Carbon::parse($allot->create_time)->toDateTimeString(),
            'type' => $allot->type,
            'create_user_id' => $allot->user->id ?? 0,
            'create_user_name' => $allot->user->realname ?? '',
            'source_warehouse_id' => $allot->source_warehouse->id ?? 0,
            'source_warehouse_name' => $allot->source_warehouse->name ?? '',
            'description' => $allot->description,
            'show_cancel' => $showCancel,
            'show_refuse' => $showRefuse,
            'show_audit' => $showAudit,
            'status' => $status,
            'status_text' => $allot->status_text,
            'type_text' => $allot->type_text,
            'quantity' => $quantity,
            'completed_quantity' => $completeQuantity,
            'un_complete_quantity' => $quantity - $completeQuantity,
            'target_warehouse_id' => $allot->target_warehouse->id ?? 0,
            'target_warehouse_name' => $allot->target_warehouse->name ?? '',
            'target_user_id' => $targetUserId,
            'target_user_name' => $targetUserName,
            'action_time' => Carbon::parse($allot->update_time)->toDateTimeString(),
            'refuse_reason' => $refuseReason
        ];

        if ($withItems) {
            // Items
            $items = self::allotItems($allot);
            $detail['items'] = $items;
        }

        return $detail;
    }

    private static function allotItems($allot)
    {
        $items = $allot->items;
        $results = [];

        $sourceWarehouseId = $allot->source_warehouse_id;
        $targetWarehouseId = $allot->target_warehouse_id;
        $targetUserId = $allot->target_user_id;

        if ($targetWarehouseId) {
            $targetWarehouseType = 'warehouse';
        } else {
            $targetWarehouseType = 'user_warehouse';
        }

        foreach ($items as $item) {
            $itemInventory = $item->inventory;
            if (empty($itemInventory)) {
                continue;
            }

            $itemCid = $itemInventory->c_id;

            if ($targetWarehouseType == 'warehouse') {
                $targetInventory = Inventory::byCid($itemCid)->byWarehouseId($targetWarehouseId)->first();
                $targetWarehouseName = $targetInventory->name ?? '';
                $targetWarehouseInventory = $targetInventory->inventory ?? 0;
            } else {
                $targetUser = UserModel::find($targetUserId);
                $targetUserInventory = UserInventory::byCid($itemCid)->byUserId($targetUserId)->byWarehouseId($sourceWarehouseId)->first();
                $targetWarehouseName = $targetUser->realname ?? '--';
                $targetWarehouseInventory = $targetUserInventory->inventory ?? 0;
            }


            $results[] = [
                'id' => $item->id,
                'name' => $item->inventory->part_category->name ?? '',
                'uom' => $item->inventory->uom,
                'c_number' => $item->inventory->c_number,
                'model' => $item->inventory->model,
                'quantity' => $item->quantity,
                'action_quantity' => $item->action_quantity,
                'active_quantity' => $item->quantity - $item->action_quantity,
                'warehouse_id' => $item->inventory->warehouse_id,
                'warehouse_name' => $item->inventory->warehouse->name,
                'inventory' => $item->inventory->inventory,
                'target_warehouse_type' => $targetWarehouseType,
                'target_warehouse_name' => $targetWarehouseName,
                'target_warehouse_inventory' => $targetWarehouseInventory
            ];
        }
        return $results;
    }

    /**
     * 审核按钮是否显示
     * 不能显示的状态: 已撤销、已拒绝、已办理
     * 权限:
     *      调拨: 目标仓库管理员
     *      申领: 目标用户
     *      分配: 目标用户
     * @param $allot
     * @return bool
     */
    private static function showAllotAudit($allot)
    {
        if (empty($allot)) {
            return false;
        }
        $status = $allot->status;
        if (in_array($status, [self::STATUS_CANCEL_CODE, self::STATUS_REJECT_CODE, self::STATUS_PROCESSED_CODE])) {
            return false;
        }

        $currentUserId = registry('user_id');

        $type = $allot->type;
        if ($type == self::TYPES_ALLOT_CODE) {
            $targetWarehouseUserIds = $allot->target_warehouse->users->pluck('user_id')->toArray();
            if (!in_array($currentUserId, $targetWarehouseUserIds)) {
                return false;
            }
        }

        if ($type == self::TYPES_ALLOCATION_CODE || $type == self::TYPES_APPLY_CODE) {
            $targetUserId = $allot->target_user_id;
            if ($currentUserId != $targetUserId) {
                return false;
            }
        }

        return true;
    }

    /**
     * 审核: 是否显示撤销按钮
     * 权限
     *      分配: 原仓库管理员
     *      调拨: 原仓库管理员
     *      申领: 发起人(创建人)
     * 状态: 仅待办理
     *
     * @param $allot
     * @return bool
     */
    public static function showAllotCancel($allot)
    {
        if (empty($allot)) {
            return false;
        }
        $status = $allot->status;
        if ($status != self::STATUS_PENDING_CODE) {
            return false;
        }
        $type = $allot->type;

        $currentUserId = registry('user_id');

        // 申领
        if ($type == self::TYPES_APPLY_CODE) {
            $createUserId = $allot->create_user_id;
            if ($createUserId != $currentUserId) {
                return false;
            }
        }

        // 分配、调拨
        if ($type == self::TYPES_ALLOCATION_CODE || $type == self::TYPES_ALLOT_CODE) {
            $sourceWareHouseId = $allot->source_warehouse_id;
            $sourceWareHouseManageUserIds = Warehouse\User::query()->where('warehouse_id', $sourceWareHouseId)->pluck('user_id')->toArray();
            if (!in_array($currentUserId, $sourceWareHouseManageUserIds)) {
                return false;
            }
        }

        return true;
    }

    /**
     * 审核拒绝
     * 状态: 已办理、已拒绝、已撤销的不能拒绝
     * 权限:
     *      调拨: 目标仓库管理员
     *      分配: 目标用户
     *      申领: 目标用户
     * @param $allot
     * @return bool
     */
    private static function showAllotRefuse($allot)
    {
        if (empty($allot)) {
            return false;
        }
        $status = $allot->status;
        if (in_array($status, [self::STATUS_PROCESSED_CODE, self::STATUS_REJECT_CODE, self::STATUS_CANCEL_CODE])) {
            return false;
        }

        $currentUserId = registry('user_id');
        $type = $allot->type;

        if ($type == self::TYPES_ALLOT_CODE) {
            $targetWarehouseUserIds = $allot->target_warehouse->users->pluck('user_id')->toArray();
            if (!in_array($currentUserId, $targetWarehouseUserIds)) {
                return false;
            }
        }

        if ($type == self::TYPES_APPLY_CODE || $type == self::TYPES_ALLOCATION_CODE) {
            $targetUserId = $allot->target_user_id;
            if ($targetUserId != $currentUserId) {
                return false;
            }
        }

        return true;
    }

    /**
     * 分配
     * 操作: 仓库管理员
     *
     */
    private static function requestAllocationData()
    {
        $data = request()->getContent();
        $arr = json_decode($data, true);

        $warehouseId = intval($arr['warehouse_id'] ?? 0);
        $userId = intval($arr['user_id'] ?? 0);
        $description = trim($arr['description'] ?? '');
        $items = $arr['items'] ?? [];

        if (!$warehouseId) {
            throw new \Exception('请选择仓库');
        }
        $warehouse = Warehouse::find($warehouseId);
        if (empty($warehouse)) {
            throw new Exception('仓库不存在');
        }
        $warehouseUserIds = $warehouse->users->pluck('user_id')->toArray();
        $currentUserId = registry('user_id');

        if ($currentUserId != 1 && !in_array($currentUserId, $warehouseUserIds)) {
            throw new \Exception('仅仓库管理员可以进行分配操作');
        }

        if (!$userId) {
            throw new \Exception('请选择分配操作的接收者');
        }

        $user = UserModel::find($userId);
        if (empty($user)) {
            throw new \Exception('分配接收者不存在');
        }

        if (!$items) {
            throw new \Exception('请选择要分配的备件');
        }

        $allocationItems = [];

        foreach ($items as $item) {
            $itemId = intval($item['id'] ?? 0);
            $itemQuantity = intval($item['quantity'] ?? 0);

            if (!$itemId || !$itemQuantity) {
                throw new \Exception("库存ID或分配数量异常(ID: {$itemId}, 数量: {$itemQuantity})");
            }

            $itemInventory = Inventory::find($itemId);
            if (empty($itemInventory)) {
                throw new \Exception('库存不存在');
            }
            $itemInventoryWarehouseId = $itemInventory->warehouse_id;
            $itemInventoryQuantity = $itemInventory->inventory;

            if ($itemInventoryWarehouseId != $warehouseId) {
                throw new \Exception("所选库存非当前仓库所有");
            }
            if ($itemQuantity > $itemInventoryQuantity) {
                throw new \Exception('分配库存超出仓库现有库存');
            }
            $allocationItems[] = [
                'id' => $itemId,
                'quantity' => $itemQuantity
            ];
        }
        $number = self::generateAllotNumber();
        return [
            'number' => $number,
            'warehouse_id' => $warehouseId,
            'user_id' => $userId,
            'description' => $description,
            'items' => $allocationItems
        ];
    }

    private static function requestApplyData()
    {
        $data = request()->getContent();
        $arr = json_decode($data, true);

        $warehouseId = intval($arr['warehouse_id'] ?? 0);
        $description = trim($arr['description'] ?? '');

        if (!$warehouseId) {
            throw new \Exception('请选择仓库');
        }

        $items = $arr['items'] ?? [];
        if (empty($items)) {
            throw new \Exception('请选择备件');
        }

        $rItems = [];
        foreach ($items as $item) {
            $itemId = intval($item['id'] ?? 0);
            $itemQuantity = intval($item['quantity'] ?? 0);

            if (!$itemId) {
                throw new \Exception("备件库不存在(#ID: {$itemId})");
            }
            $inventory = Inventory::find($itemId);
            if (empty($inventory)) {
                throw new \Exception("备件库不存在(#ID: {$itemId})");
            }
            if ($inventory->warehouse_id != $warehouseId) {
                throw new \Exception("所选备件库存非原仓库所有");
            }
            if ($itemQuantity <= 0) {
                throw new \Exception('申领数量错误');
            }

            // 数量判断
            $inventoryQuantity = $inventory->inventory;
            if ($inventoryQuantity < $itemQuantity) {
                throw new \Exception("申领数量不能大于库存数量(库存数量: {$inventoryQuantity}, 申领数量: {$itemQuantity})");
            }

            $rItems[] = [
                'id' => $itemId,
                'quantity' => $itemQuantity
            ];
        }

        $number = self::generateAllotNumber();
        return [
            'number' => $number,
            'source_warehouse_id' => $warehouseId,
            'target_user_id' => registry('user_id'),
            'description' => $description,
            'items' => $rItems
        ];
    }

    private static function requestAllotData()
    {
        $data = request()->getContent();
        $arr = json_decode($data, true);

        $sourceWarehouseId = intval($arr['source_warehouse_id'] ?? 0);
        $targetWarehouseId = intval($arr['target_warehouse_id'] ?? 0);
        $description = trim($arr['description'] ?? '');

        if (!$sourceWarehouseId || !$targetWarehouseId) {
            throw new \Exception('请指定原仓库和目标仓库');
        }
        if ($sourceWarehouseId == $targetWarehouseId) {
            throw new \Exception('原仓库和目标仓库不能相同');
        }

        $currentWarehouseIds = WarehouseRepo::userWarehouseIds(registry('user_id'));
        if (!in_array($sourceWarehouseId, $currentWarehouseIds)) {
            throw new \Exception('你不是原仓库管理员， 不能进行调拨操作');
        }

        $targetWarehouse = Warehouse::find($targetWarehouseId);
        if (empty($targetWarehouse)) {
            throw new \Exception('调拨失败，目标仓库不存在');
        }

        $items = $arr['items'] ?? [];
        if (empty($items)) {
            throw new \Exception('请选择备件');
        }

        $rItems = [];
        foreach ($items as $item) {
            $itemId = intval($item['id'] ?? 0);
            $itemQuantity = intval($item['quantity'] ?? 0);

            if (!$itemId) {
                throw new \Exception("备件库不存在(#ID: {$itemId})");
            }
            $inventory = Inventory::find($itemId);
            if (empty($inventory)) {
                throw new \Exception("备件库不存在(#ID: {$itemId})");
            }
            if ($inventory->warehouse_id != $sourceWarehouseId) {
                throw new \Exception("所选备件库存非原仓库所有");
            }
            if ($itemQuantity <= 0) {
                throw new \Exception('调拨数量不能为0');
            }
            // 数量判断
            $inventoryQuantity = $inventory->inventory;
            if ($inventoryQuantity < $itemQuantity) {
                throw new \Exception("调拨数量不能大于库存数量(库存数量: {$inventoryQuantity}, 调拨数量: {$itemQuantity})");
            }
            $rItems[] = [
                'id' => $itemId,
                'quantity' => $itemQuantity
            ];
        }
        $number = self::generateAllotNumber();
        return [
            'number' => $number,
            'source_warehouse_id' => $sourceWarehouseId,
            'target_warehouse_id' => $targetWarehouseId,
            'description' => $description,
            'items' => $rItems
        ];
    }

    private static function generateAllotNumber($id = 0)
    {
        $allot = InventoryAllot::find($id);
        if (!empty($allot)) {
            return $allot->number;
        }
        $number = 'A' . date('Ymd') . rand(100000, 999999);
        $numberInventory = InventoryAllot::byNumber($number)->first();
        if (!empty($numberInventory)) {
            return self::generateAllotNumber($id);
        }
        return $number;
    }

    // 批量出库
    public static function batchOut()
    {
        $data = self::requestOutData();
        foreach ($data as $item) {
            $inventory = $item['inventory'];
            $quantity = $item['quantity'];

            $inventory->inventory = $inventory->inventory - $quantity;
            $inventory->saveOrFail();

            $historyData = [
                'inventory_id' => $inventory->id,
                'source_warehouse_id' => $inventory->warehouse_id,
                'source_warehouse_type' => self::TYPE,
                'count' => $quantity,
                'action' => self::ACTION_OUTSIDE,
                'type' => $item['type'],
                'description' => $item['description']
            ];

            $history = new InventoryHistory();
            $history->fill($historyData);
            $history->saveOrFail();
        }
    }

    public static function show($id)
    {
        $inventory = Inventory::find($id);
        if (empty($inventory)) {
            throw new \Exception('库存信息不存在');
        }
        $inventory->with(['part_category', 'warehouse', 'user']);
        return self::getDetail($inventory);
    }

    public static function singleOut()
    {
        $id = intval(request('id', 0));
        if (!self::checkSelfWarehouse($id)) {
            throw new \Exception("你不是当前备件所在仓库管理员，无法进行出库操作");
        }

        $dbInventory = Inventory::find($id);

        $type = trim(request('type', ''));
        $inventory = intval(request('inventory', ''));
        $description = trim(request('description', ''));

        $params = self::params();
        $outTypes = $params['out_types'] ?? [];
        if (!in_array($type, $outTypes)) {
            throw new \Exception('出库失败，类型错误');
        }

        if ($inventory <= 0 || $inventory > $dbInventory->inventory) {
            throw new \Exception('出库失败，出库数量错误');
        }

        $dbInventory->inventory = $dbInventory->inventory - $inventory;
        $dbInventory->saveOrFail();

        $historyData = [
            'inventory_id' => $dbInventory->id,
            'source_warehouse_id' => $dbInventory->warehouse_id,
            'source_warehouse_type' => self::TYPE,
            'count' => $inventory,
            'action' => self::ACTION_OUTSIDE,
            'type' => $type,
            'description' => $description
        ];

        $history = new InventoryHistory();
        $history->fill($historyData);
        $history->saveOrFail();
    }

    // 库存数量统计
    public static function auditCounts()
    {
        $auditCountsData = InventoryAllot::query()
            ->select('status', DB::raw('count(*) as count'))
            ->where('audit_user_id', registry('user_id'))
            ->groupBy('status')
            ->get()
            ->keyBy('status')
            ->toArray();
        $auditPendingCount = $auditProcessingCount = $auditProcessedCount = 0;
        foreach ($auditCountsData as $status => $item) {
            if ($status == self::STATUS_PENDING_CODE) {
                $auditPendingCount += $item['count'];
            }
            if ($status == self::STATUS_PROCESSING_CODE) {
                $auditProcessingCount += $item['count'];
            }
            if ($status == self::STATUS_PROCESSED_CODE) {
                $auditProcessedCount += $item['count'];
            }
        }

        $applyCountsData = InventoryAllot::query()
            ->select('status', DB::raw('count(*) as count'))
            ->where('create_user_id', registry('user_id'))
            ->groupBy('status')
            ->get()
            ->keyBy('status')
            ->toArray();

        $applyPendingCount = $applyProcessingCount = $applyProcessedCount = 0;
        foreach ($applyCountsData as $status => $item) {
            if ($status == self::STATUS_PENDING_CODE) {
                $applyPendingCount += $item['count'];
            }
            if ($status == self::STATUS_PROCESSING_CODE) {
                $applyProcessingCount += $item['count'];
            }
            if ($status == self::STATUS_PROCESSED_CODE) {
                $applyProcessedCount += $item['count'];
            }
        }

        return [
            'audit' => [
                self::STATUS_PENDING_CODE => $auditPendingCount,
                self::STATUS_PROCESSING_CODE => $auditProcessingCount,
                self::STATUS_PROCESSED_CODE => $auditProcessedCount
            ],
            'apply' => [
                self::STATUS_PENDING_CODE => $applyPendingCount,
                self::STATUS_PROCESSING_CODE => $applyProcessingCount,
                self::STATUS_PROCESSED_CODE => $applyProcessedCount
            ]
        ];
    }

    // 批量入库
    public static function batchStorage()
    {
        $data = self::requestData();
        foreach ($data as $item) {
            $itemWarehouseId = $item['warehouse_id'];
            $cid = $item['c_id'];
            $inventory = $item['inventory'];
            $historyInventory = $inventory;
            $dbInventory = Inventory::byWarehouseId($itemWarehouseId)->byCid($cid)->first();
            if (empty($dbInventory)) {
                $dbInventory = new Inventory();
            } else {
                $inventory = $inventory + $dbInventory->inventory;
            }
            $item['inventory'] = $inventory;

            $dbInventory->fill($item);
            $dbInventory->saveOrFail();

            $historyData = [
                'inventory_id' => $dbInventory->id,
                'target_warehouse_id' => $itemWarehouseId,
                'target_warehouse_type' => self::TYPE,
                'count' => $historyInventory,
                'action' => self::ACTION_STORAGE,
                'type' => $item['storage_type']
            ];

            $history = new InventoryHistory();
            $history->fill($historyData);
            $history->saveOrFail();
        }
    }

    // 单一入库
    public static function singleStorage()
    {
        $id = intval(request('id', 0));
        if (!self::checkSelfWarehouse($id)) {
            throw new \Exception("你不是当前备件所在仓库管理员，无法进行入库操作");
        }
        $inventory = intval(request('inventory', 0));
        $description = trim(request('description', ''));
        $type = trim(request('type', ''));
        if ($inventory <= 0) {
            throw new \Exception("入库数量错误，请检查");
        }
        $dbInventory = Inventory::find($id);
        $dbInventory->inventory = $dbInventory->inventory + $inventory;
        if ($description) {
            $dbInventory->description = $description;
        }
        if ($type) {
            $dbInventory->storage_type = $type;
        }
        $dbInventory->saveOrFail();

        if (!$type) {
            $type = $dbInventory->storage_type;
        }

        $historyData = [
            'inventory_id' => $dbInventory->id,
            'target_warehouse_id' => $dbInventory->warehouse_id,
            'target_warehouse_type' => self::TYPE,
            'count' => $inventory,
            'action' => self::ACTION_STORAGE,
            'type' => $type
        ];

        $history = new InventoryHistory();
        $history->fill($historyData);
        $history->saveOrFail();
    }

    // 检查备件仓库是否是当前用户所有
    public static function checkSelfWarehouse($id): bool
    {
        if (!$id) {
            return false;
        }
        $inventory = Inventory::find($id);
        if (empty($inventory)) {
            return false;
        }
        $warehouseId = $inventory->warehouse_id;
        if (!in_array($warehouseId, WarehouseRepo::userWarehouseIds(registry('user_id')))) {
            return false;
        }
        return true;
    }

    // 指定仓库下的所有备件信息
    public static function warehouseInventories()
    {
        $warehouseId = request('warehouse_id', 0);
        if (!in_array($warehouseId, WarehouseRepo::userWarehouseIds(registry('user_id')))) {
            throw new \Exception("获取失败，你不是所选仓库管理员(#ID: {$warehouseId})");
        }
        $builder = Inventory::byWarehouseId($warehouseId);
        $builder->with(['part_category', 'warehouse', 'user']);
        $inventories = $builder->get();
        if (!$inventories->count()) {
            return [];
        }
        $results = [];
        foreach ($inventories as $inventory) {
            $results[] = self::getDetail($inventory);
        }
        return $results;
    }

    public static function safeQuantity($id)
    {
        if (!self::checkSelfWarehouse($id)) {
            throw new \Exception("你不是当前备件所在仓库管理员，无法修改安全库存");
        }
        $inventory = intval(request('inventory', 0));
        if ($inventory <= 0) {
            throw new \Exception("安全库存需大于0");
        }
        $dbInventory = Inventory::find($id);
        $dbInventory->safe_inventory = $inventory;
        $dbInventory->saveOrFail();
    }

    private static function requestOutData()
    {
        $data = request()->getContent();
        $arr = json_decode($data, true);

        $params = self::params();
        $outTypes = $params['out_types'] ?? [];
        $warehouseId = $arr['warehouse_id'] ?? 0;
        if (!in_array($warehouseId, WarehouseRepo::userWarehouseIds(registry('user_id')))) {
            throw new \Exception("出库失败，仓库选择错误或你不是所选仓库管理员(#ID: {$warehouseId})");
        }
        $type = $arr['type'] ?? '';
        if (!$type) {
            throw new \Exception('请选择出库原因');
        }
        if (!in_array($type, $outTypes)) {
            throw new \Exception('出库原因错误');
        }
        $description = trim($arr['description'] ?? '');
        $items = $arr['part_categories'] ?? [];
        $results = [];
        foreach ($items as $item) {
            $itemId = intval($item['id'] ?? 0);
            $itemQuantity = intval($item['quantity'] ?? 0);

            if (!$itemId || !$itemQuantity) {
                throw new \Exception('出库失败，参数错误');
            }
            $inventory = Inventory::find($itemId);
            if (empty($inventory)) {
                throw new \Exception("出库失败，备件错误(#ID: {$itemId})");
            }
            $inventoryQuantity = $inventory->inventory;
            if ($inventoryQuantity < $itemQuantity) {
                throw new \Exception("出库失败，出库数量不能大于现有库存量(#ID: {$itemId})");
            }
            $results[] = [
                'inventory' => $inventory,
                'quantity' => $itemQuantity,
                'type' => $type,
                'description' => $description
            ];
        }
        return $results;
    }

    private static function requestData()
    {
        $data = request()->getContent();
        $arr = json_decode($data, true);

        $warehouseId = $arr['warehouse_id'] ?? 0;
        if (!$warehouseId) {
            throw new \Exception('请选择入库仓库');
        }
        $warehouse = Warehouse::find($warehouseId);
        if (empty($warehouse)) {
            throw new \Exception("所选仓库不存在(#: {$warehouseId})");
        }
        $currentWarehouseIds = WarehouseRepo::userWarehouseIds(registry('user_id'));
        if (!in_array($warehouseId, $currentWarehouseIds)) {
            throw new \Exception('所选仓库非当前登录账号所有');
        }
        $type = $arr['type'] ?? '';
        $params = self::params();
        $storageTypes = $params['storage_types'] ?? [];
        if (!in_array($type, $storageTypes)) {
            throw new \Exception('入库类型错误');
        }

        $partCategories = $arr['part_categories'] ?? [];
        if (!$partCategories) {
            throw new \Exception('请选择入库备件');
        }
        $pcs = [];
        foreach ($partCategories as $partCategory) {
            $itemId = $partCategory['id'];
            $itemQuantity = intval($partCategory['quantity']);
            if (!$itemId) {
                throw new \Exception("备件信息错误: (#ID: {$itemId})");
            }
            $itemPartCategory = PartCategory::find($itemId);
            if (empty($itemPartCategory)) {
                throw new \Exception("备件不存在: (#ID: {$itemId})");
            }
            if ($itemQuantity <= 0) {
                throw new \Exception("入库数量错误: (#ID: {$itemId}, 数量: {$itemQuantity})");
            }
            $pcs[] = [
                'data' => $itemPartCategory,
                'quantity' => $itemQuantity
            ];
        }
        if (empty($pcs)) {
            throw new \Exception('入库备件为空');
        }

        $results = [];
        foreach ($pcs as $pc) {
            $data = $pc['data'];
            $results[] = [
                'warehouse_id' => $warehouseId,
                'storage_type' => $type,
                'c_id' => $data->id,
                'c_number' => $data->c_number,
                'type' => $data->type,
                'model' => $data->model,
                'uom' => $data->uom,
                'sale_price' => $data->sale_price,
                'out_price' => $data->out_price,
                'description' => $data->description,
                'inventory' => $pc['quantity'],
                'create_user_id' => registry('user_id')
            ];
        }
        return $results;
    }

    // 添加审核历史
    private static function addAuditHistory($id)
    {
        $inventoryAllot = InventoryAllot::find($id);
        if (empty($inventoryAllot)) {
            return;
        }
        $quantity = $inventoryAllot->items->sum('quantity');
        $description = $inventoryAllot->description;
        $userId = $inventoryAllot->create_user_id;

        $historyData = [
            'inventory_allot_id' => $id,
            'quantity' => $quantity,
            'description' => $description,
            'create_user_id' => $userId
        ];
        $history = new InventoryAuditHistory();
        $history->fill($historyData);
        $history->saveOrFail();
    }

    /**
     * 发送通知
     * 接收对象
     *      撤销: 目标仓库管理员
     *      审核(通过): 创建人
     *      拒绝: 创建人
     *      发起:
     *          调拨: 目标仓库管理员
     *          分配: 目标用户
     *          申领: 目标用户
     *          退回: 目标仓库管理员
     * @param $id
     */
    private static function pushNotifyMessage($id)
    {
        $inventoryAllot = InventoryAllot::find($id);
        if (empty($inventoryAllot)) {
            return;
        }
        $type = $inventoryAllot->type;
        $status = $inventoryAllot->status;

        $pushUserIds = [];

        // 原仓库管理员
        $sourceWarehouseUserIds = $inventoryAllot->source_warehouse->users->pluck('user_id')->toArray();
        // 目标仓库管理员
        try {
            $targetWarehouseUserIds = $inventoryAllot->target_warehouse->users->pluck('user_id')->toArray();
        } catch (\Exception $e) {
            $targetWarehouseUserIds = [];
        }
        // 创建人
        $createUserIds = [$inventoryAllot->create_user_id];
        // 目标用户
        $targetUserIds = [$inventoryAllot->target_user_id];

        if ($status == self::STATUS_CANCEL_CODE) {
            $pushUserIds = $targetWarehouseUserIds;
        } elseif ($status == self::STATUS_PROCESSED_CODE) {
            $pushUserIds = $createUserIds;
        } elseif ($status == self::STATUS_REJECT_CODE) {
            $pushUserIds = $createUserIds;
        } elseif ($status == self::STATUS_PENDING_CODE) {
            if ($type == self::TYPES_ALLOT_CODE) {
                $pushUserIds = $targetWarehouseUserIds;
            } elseif ($type == self::TYPES_ALLOCATION_CODE) {
                $pushUserIds = $targetUserIds;
            } elseif ($type == self::TYPES_APPLY_CODE) {
                $pushUserIds = $targetUserIds;
            } elseif ($type == self::TYPES_BACK_CODE) {
                $pushUserIds = $targetWarehouseUserIds;
            }
        }

        $title = '';
        $content = '';
        if ($type == self::TYPES_ALLOT_CODE) {
            if ($status == self::STATUS_PENDING_CODE) {
                $title = '调拨通知';
                $content = '您有一条调拨通知待审核';
            } elseif ($status == self::STATUS_CANCEL_CODE) {
                $title = '调拨撤销通知';
                $content = '您有一条调拨撤销通知';
            } elseif ($status == self::STATUS_PROCESSED_CODE) {
                $title = '调拨审核通知';
                $content = '您有一条调拨申请已审核';
            } elseif ($status == self::STATUS_REJECT_CODE) {
                $title = '调拨拒绝通知';
                $content = '您有一条调拨申请已被拒绝';
            }
        }
        if ($type == self::TYPES_ALLOCATION_CODE) {
            if ($status == self::STATUS_PENDING_CODE) {
                $title = '分配通知';
                $content = '您有一条分配通知待审核';
            } elseif ($status == self::STATUS_CANCEL_CODE) {
                $title = '分配撤销通知';
                $content = '您有一条分配撤销通知';
            } elseif ($status == self::STATUS_PROCESSED_CODE) {
                $title = '分配审核通知';
                $content = '您有一条分配申请已审核';
            } elseif ($status == self::STATUS_REJECT_CODE) {
                $title = '分配拒绝通知';
                $content = '您有一条分配申请已被拒绝';
            }
        }
        if ($type == self::TYPES_APPLY_CODE) {
            if ($status == self::STATUS_PENDING_CODE) {
                $title = '申领通知';
                $content = '您有一条申领通知待审核';
            } elseif ($status == self::STATUS_CANCEL_CODE) {
                $title = '申领撤销通知';
                $content = '您有一条申领撤销通知';
            } elseif ($status == self::STATUS_PROCESSED_CODE) {
                $title = '申领审核通知';
                $content = '您有一条申领申请已审核';
            } elseif ($status == self::STATUS_REJECT_CODE) {
                $title = '申领拒绝通知';
                $content = '您有一条申领申请已被拒绝';
            }
        }
        if ($type == self::TYPES_BACK_CODE) {
            if ($status == self::STATUS_PENDING_CODE) {
                $title = '退回通知';
                $content = '您有一条退回通知待审核';
            } elseif ($status == self::STATUS_CANCEL_CODE) {
                $title = '退回撤销通知';
                $content = '您有一条退回撤销通知';
            } elseif ($status == self::STATUS_PROCESSED_CODE) {
                $title = '退回审核通知';
                $content = '您有一条退回申请已审核';
            } elseif ($status == self::STATUS_REJECT_CODE) {
                $title = '退回拒绝通知';
                $content = '您有一条退回申请已被拒绝';
            }
        }
        $contentArr = [
            'number' => $inventoryAllot->number,
            'source_warehouse_id' => $inventoryAllot->source_warehouse_id,
            'source_warehouse_name' => $inventoryAllot->source_warehouse->name ?? '',
            'target_warehouse_id' => $inventoryAllot->target_warehouse_id,
            'target_warehouse_name' => $inventoryAllot->target_warehouse->name ?? '',
            'create_user_id' => $inventoryAllot->create_user_id,
            'create_user_name' => $inventoryAllot->user->realname ?? '',
            'create_time' => Carbon::parse($inventoryAllot->create_time)->toDateTimeString()
        ];
        // $content = json_encode($contentArr);
        NotificationRepo::multiStore($pushUserIds, $id, NotificationRepo::TYPE_INVENTORY, $title, $content);
    }
}